/*=====================================================
Critical Links
Auto Configuration Server

Authors:
	Rui Eduardo Gouveia Gil - r-gil@critical-links.com

Date: 2010
Version 0.1
=======================================================*/
#include "httpd/HttpUserManager.h"
#include "database/DataBase.h"
#include "httpd/SGui.h"
#include "ACSConfig.h"

namespace acs
{

rgcpp::Sequence<int> SGui::fileID(1);
std::string SGui::TMP_FILE_PEFIX="/tmp/eacs_temp_";

std::vector<std::string> SGui::createGroup(std::string name, std::string description)
{
	std::vector<std::string> result;

	boost::trim(name);

	if(name.empty())
	{
		result.push_back("0");
		result.push_back("Invalid group name!");
	}
	else if(DataBase::getInstance()->existsGroup(name))
	{
		result.push_back("0");
		result.push_back("Already exists a group with the same name. Please choose a different one!");
	}
	else
	{
		try
		{
			Group group;
			group.setName(name);
			group.setDescription(description);
			group = DataBase::getInstance()->createGroup(group);

			result.push_back("1");
			result.push_back(group.toXML());
		}
		catch (rgcpp::Exception& e)
		{
			result.push_back("-1");
			result.push_back("Server Internal Error! Please check the logs for more information.");
		}
	}

	return result;
}

std::vector<std::string> SGui::saveGroup(int id, std::string name, std::string description)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsGroup(id))
	{
		result.push_back("-1");
		result.push_back("Invalid group!");

		return result;
	}

	boost::trim(name);

	Group group = DataBase::getInstance()->getGroup(id);
	if(name.empty())
	{
		result.push_back("0");
		result.push_back("Invalid group name!");
	}
	else if(group.getName().compare(name)!=0 &&  DataBase::getInstance()->existsGroup(name))
	{
		result.push_back("0");
		result.push_back("Already exists a group with the same name. Please choose a different one!");
	}
	else
	{
		try
		{
			group.setName(name);
			group.setDescription(description);
			DataBase::getInstance()->updateGroup(group);

			result.push_back("1");
			result.push_back(group.toXML());
		}
		catch (rgcpp::Exception& e)
		{
			result.push_back("-1");
			result.push_back("Server Internal Error! Please check the logs for more information.");
		}
	}

	return result;
}

std::vector<std::string> SGui::getGroup(int id)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsGroup(id))
	{
		result.push_back("-1");
		result.push_back("Invalid group!");
	}
	else
	{	Group group = DataBase::getInstance()->getGroup(id);
		result.push_back("1");
		result.push_back(group.toXMLFull());
	}

	return result;
}

std::vector<std::string> SGui::getGroups()
{
	std::vector<std::string> result;

	std::vector<Group> groups = DataBase::getInstance()->listGroups("");
	for(std::vector<Group>::iterator group = groups.begin(); group !=  groups.end(); group++)
		result.push_back(group->toXML());

	return result;
}

std::vector<std::string> SGui::removeGroup(int id)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsGroup(id))
	{
		result.push_back("-1");
		result.push_back("Invalid group!");
	}
	else if(DataBase::getInstance()->getGroupDevicesCount(id) != 0)
	{
		result.push_back("-1");
		result.push_back("Unable to delete this group because it contains devices!!");
	}
	else
	{
		DataBase::getInstance()->removeGroup(id);
		result.push_back("1");
	}

	return result;
}

std::vector<std::string> SGui::createConfiguration(std::string name, bool rwan, bool rusers, std::string description)
{
	std::vector<std::string> result;

	boost::trim(name);

	if(name.empty())
	{
		result.push_back("0");
		result.push_back("Invalid configuration name!");
	}
	else if(DataBase::getInstance()->existsConfiguration(name))
	{
		result.push_back("0");
		result.push_back("Already exists a configuration with the same name. Please choose a different one!");
	}
	else
	{
		try
		{
			Configuration configuration;
			configuration.setRWan(rwan);
			configuration.setRUsers(rusers);
			configuration.setName(name);
			configuration.setDescription(description);
			configuration = DataBase::getInstance()->createConfiguration(configuration);

			result.push_back("1");
			result.push_back(configuration.toXML());
		}
		catch (rgcpp::Exception& e)
		{
			result.push_back("-1");
			result.push_back("Server Internal Error! Please check the logs for more information.");
		}
	}

	return result;
}

std::vector<std::string> SGui::saveConfiguration(int id, std::string name, bool rwan, bool rusers, std::string description)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsConfiguration(id))
	{
		result.push_back("-1");
		result.push_back("Invalid configuration!");

		return result;
	}

	boost::trim(name);

	Configuration configuration = DataBase::getInstance()->getConfiguration(id);
	if(name.empty())
	{
		result.push_back("0");
		result.push_back("Invalid configuration name!");
	}
	else if(configuration.getName().compare(name)!=0 &&  DataBase::getInstance()->existsConfiguration(name))
	{
		result.push_back("0");
		result.push_back("Already exists a configuration with the same name. Please choose a different one!");
	}
	else
	{
		try
		{
			if(!configuration.getConfigFileName().empty() && (configuration.isRWan() != rwan || configuration.isRUsers() != rusers))
			{
				std::string restoreOptions = rgcpp::ToString(rwan)+"-"+rgcpp::ToString(rusers);
				configuration.setSHA(SGui::generateBackupSHA(ACSConfig::getDataDirectory()+std::string("Configuration_BackupRestore_")+rgcpp::ToString(configuration.getID()),restoreOptions));
				DataBase::getInstance()->setStatusDevicesWithConfiguration(id,DS_PENDING_TASKS);
			}

			configuration.setRWan(rwan);
			configuration.setRUsers(rusers);
			configuration.setName(name);
			configuration.setDescription(description);
			DataBase::getInstance()->updateConfiguration(configuration);

			result.push_back("1");
			result.push_back(configuration.toXML());
		}
		catch (rgcpp::Exception& e)
		{
			result.push_back("-1");
			result.push_back("Server Internal Error! Please check the logs for more information.");
		}
	}

	return result;
}

std::vector<std::string> SGui::clearConfigurationBackupRestoreFile(int id)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsConfiguration(id))
	{
		result.push_back("-1");
		result.push_back("Invalid configuration!");

		return result;
	}

	Configuration configuration = DataBase::getInstance()->getConfiguration(id);
	try
	{
		configuration.setConfigFileName("");
		DataBase::getInstance()->updateConfiguration(configuration);

		result.push_back("1");
		result.push_back(configuration.toXML());
	}
	catch (rgcpp::Exception& e)
	{
		result.push_back("-1");
		result.push_back("Server Internal Error! Please check the logs for more information.");
	}

	return result;
}


std::vector<std::string> SGui::getConfiguration(int id)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsConfiguration(id))
	{
		result.push_back("-1");
		result.push_back("Invalid configuration!");
	}
	else
	{	Configuration configuration = DataBase::getInstance()->getConfiguration(id);
		result.push_back("1");
		result.push_back(configuration.toXMLFull());
	}

	return result;
}

std::vector<std::string> SGui::getConfigurations()
{
	std::vector<std::string> result;

	std::vector<Configuration> configurations = DataBase::getInstance()->listConfigurations("");
	for(std::vector<Configuration>::iterator configuration = configurations.begin(); configuration !=  configurations.end(); configuration++)
		result.push_back(configuration->toXML());

	return result;
}

std::vector<std::string> SGui::removeConfiguration(int id)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsConfiguration(id))
	{
		result.push_back("-1");
		result.push_back("Invalid configuration!");

		return result;
	}
	else if(DataBase::getInstance()->getConfigurationDevicesCount(id) != 0)
	{
		result.push_back("-1");
		result.push_back("Unable to delete this configuration. It is in use by a device.");
	}
	else
	{
		DataBase::getInstance()->removeConfiguration(id);
		result.push_back("1");
	}

	return result;
}

std::vector<std::string> SGui::createDevice(std::string name, std::string license, int configID, int configMode, int groupID, std::string description, std::string actionPeriod)
{
	std::vector<std::string> result;

	boost::trim(name);
	boost::trim(license);
	boost::trim(actionPeriod);

	if(name.empty())
	{
		result.push_back("0");
		result.push_back("Invalid device name!");
	}
	else if(DataBase::getInstance()->existsDevice(name))
	{
		result.push_back("0");
		result.push_back("The device name is already in use! Please choose a different one.!");
	}
	else  if(license.empty())
	{
		result.push_back("0");
		result.push_back("Invalid device license!");
	}
	else
	{
		try
		{
			Device device;
			device.setName(name);
			if((DeviceConfigMode)configMode == DCM_MANUAL)
				device.setStatus(DS_PENDING_CONFIRMATION);
			else
				device.setStatus(DS_PENDING_TASKS);
			device.setConfigID(configID);
			device.setLicenseID(license);
			device.setGroupID(groupID);
			device.setDescription(description);
			device.setActionPeriod(actionPeriod);
			device.setConfigMode((DeviceConfigMode)configMode);

			device = DataBase::getInstance()->createDevice(device);

			result.push_back("1");
			result.push_back(device.toXML());
		}
		catch (rgcpp::Exception& e)
		{
			result.push_back("-1");
			result.push_back("Server Internal Error! Please check the logs for more information.");
		}
	}

	return result;
}

std::vector<std::string> SGui::saveDevice(int id, std::string name, std::string license, int configID, int configMode, int groupID, std::string description, std::string actionPeriod)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsDevice(id))
	{
		result.push_back("-1");
		result.push_back("Invalid device!");

		return result;
	}

	Device device = DataBase::getInstance()->getDevice(id);

	boost::trim(name);
	boost::trim(license);
	boost::trim(actionPeriod);

	if(name.empty())
	{
		result.push_back("0");
		result.push_back("Invalid device name!");
	}
	else if(device.getName().compare(name)!=0 && DataBase::getInstance()->existsDevice(name))
	{
		result.push_back("0");
		result.push_back("The device name is already in use! Please choose a different one.!");
	}
	else  if(license.empty())
	{
		result.push_back("0");
		result.push_back("Invalid device license!");
	}
	else
	{
		try
		{
			device.setName(name);
			if(device.getStatus() == DS_SYNCHRONIZED && configID != device.getConfigID())
			{
				if(device.getConfigMode() == DCM_MANUAL)
					device.setStatus(DS_PENDING_CONFIRMATION);
				else
					device.setStatus(DS_PENDING_TASKS);
			}
			device.setConfigID(configID);
			device.setGroupID(groupID);
			device.setLicenseID(license);
			device.setDescription(description);
			device.setActionPeriod(actionPeriod);
			device.setConfigMode((DeviceConfigMode)configMode);

			DataBase::getInstance()->updateDevice(device);

			result.push_back("1");
			result.push_back(device.toXML());
		}
		catch (rgcpp::Exception& e)
		{
			result.push_back("-1");
			result.push_back("Server Internal Error! Please check the logs for more information.");
		}
	}

	return result;
}

std::vector<std::string> SGui::validateDevice(int id)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsDevice(id))
	{
		result.push_back("-1");
		result.push_back("Invalid device!");

		return result;
	}

	Device device = DataBase::getInstance()->getDevice(id);
	try
	{
		if(device.getStatus() == DS_PENDING_VALIDATION)
		{
			if(device.getConfigMode() == DCM_MANUAL)
				device.setStatus(DS_PENDING_CONFIRMATION);
			else
				device.setStatus(DS_PENDING_TASKS);
		}
		else if(device.getStatus() == DS_PENDING_CONFIRMATION)
			device.setStatus(DS_PENDING_TASKS);
		DataBase::getInstance()->updateDevice(device);

		result.push_back("1");
		result.push_back(device.toXML());
	}
	catch (rgcpp::Exception& e)
	{
		result.push_back("-1");
		result.push_back("Server Internal Error! Please check the logs for more information.");
	}

	return result;
}

std::vector<std::string> SGui::backupDevice(int id, bool sounds)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsDevice(id))
	{
		result.push_back("-1");
		result.push_back("Invalid device!");

		return result;
	}

	Device device = DataBase::getInstance()->getDevice(id);
	try
	{
		if(device.getStatus() != DS_EXECUTING_BACKUP)
		{
			if(sounds)
				device.setStatus(DS_PENDING_BACKUP_WITH_SOUNDS);
			else
				device.setStatus(DS_PENDING_BACKUP);
			DataBase::getInstance()->updateDevice(device);
		}

		result.push_back("1");
		result.push_back(device.toXML());
	}
	catch (rgcpp::Exception& e)
	{
		result.push_back("-1");
		result.push_back("Server Internal Error! Please check the logs for more information.");
	}

	return result;
}

std::vector<std::string> SGui::getDevice(int id)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsDevice(id))
	{
		result.push_back("-1");
		result.push_back("Invalid device!");
	}
	else
	{	Device device = DataBase::getInstance()->getDevice(id);
		result.push_back("1");
		result.push_back(device.toXMLFull());
	}

	return result;
}

std::vector<std::string> SGui::getDevices()
{
	std::vector<std::string> result;

	std::vector<Device> devices = DataBase::getInstance()->listDevices("");
	for(std::vector<Device>::iterator device = devices.begin(); device !=  devices.end(); device++)
		result.push_back(device->toXML());

	return result;
}

std::vector<std::string> SGui::getGroupDevices(int groupID)
{
	std::vector<std::string> result;

	std::vector<Device> devices = DataBase::getInstance()->getGroupDevices(groupID);
	for(std::vector<Device>::iterator device = devices.begin(); device !=  devices.end(); device++)
		result.push_back(device->toXML());

	return result;
}

std::vector<std::string> SGui::getConfigurationDevices(int configID)
{
	std::vector<std::string> result;

	std::vector<Device> devices = DataBase::getInstance()->getConfigurationDevices(configID);
	for(std::vector<Device>::iterator device = devices.begin(); device !=  devices.end(); device++)
		result.push_back(device->toXML());

	return result;
}

std::vector<std::string> SGui::removeDevice(int id)
{
	std::vector<std::string> result;

	try
	{
		DataBase::getInstance()->removeDevice(id);
		result.push_back("1");
	}
	catch (rgcpp::Exception& e)
	{
		result.push_back("-1");
		result.push_back("Server Internal Error! Please check the logs for more information.");
	}

	return result;
}

std::vector<std::string> SGui::setDeviceCommands(int id, std::vector<std::string> commands)
{
	std::vector<std::string> result;
	if(!DataBase::getInstance()->existsDevice(id))
	{
		result.push_back("-1");
		result.push_back("Invalid device!");
		return result;
	}

	std::string str;
	std::string command;
	std::vector<std::string> arguments;
	DataBase::getInstance()->removeDeviceCommands(id);
	for(std::vector<std::string>::iterator cmd = commands.begin(); cmd!=commands.end(); cmd++)
	{
		str = *cmd;
		boost::trim(str);

		rgcpp::StringTokenizer tokens(str,' ');

		command = tokens.nextToken();

		arguments.clear();
		while(tokens.hasMoreElements())
		{
			str = tokens.nextToken();
			boost::trim(str);
			if(!str.empty())
				arguments.push_back(str);
		}

		DataBase::getInstance()->addDeviceCommand(Command(id,command,arguments));
	}

	Device device = DataBase::getInstance()->getDevice(id);
	if(device.getStatus() == DS_SYNCHRONIZED)
	{
		if(device.getConfigMode() == DCM_MANUAL)
			device.setStatus(DS_PENDING_CONFIRMATION);
		else
			device.setStatus(DS_PENDING_TASKS);
	}
	DataBase::getInstance()->updateDevice(device);

	result.push_back("1");
	return result;
}

std::vector<std::string> SGui::addDeviceCommand(int id, std::string command, std::vector<std::string> arguments)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsDevice(id))
	{
		result.push_back("-1");
		result.push_back("Invalid device!");
		return result;
	}

	if(!command.empty())
	{
		DataBase::getInstance()->addDeviceCommand(Command(id,command,arguments));

		Device device = DataBase::getInstance()->getDevice(id);
		if(device.getStatus() == DS_SYNCHRONIZED)
		{
			if(device.getConfigMode() == DCM_MANUAL)
				device.setStatus(DS_PENDING_CONFIRMATION);
			else
				device.setStatus(DS_PENDING_TASKS);
		}
		DataBase::getInstance()->updateDevice(device);
	}

	result.push_back("1");
	return result;
}

std::vector<std::string> SGui::getDeviceCommands(int id)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsDevice(id))
	{
		result.push_back("-1");
		result.push_back("Invalid device!");
		return result;
	}

	std::vector<Command> commands = DataBase::getInstance()->getDeviceCommands(id);
	for(std::vector<Command>::iterator itr=commands.begin(); itr!=commands.end(); itr++)
		result.push_back(itr->toXML());

	return result;
}

std::vector<std::string> SGui::removeDeviceCommand(int id, int index)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsDevice(id))
	{
		result.push_back("-1");
		result.push_back("Invalid device!");
		return result;
	}

	DataBase::getInstance()->removeDeviceCommand(id,index);

	result.push_back("1");
	return result;
}

std::vector<std::string> SGui::removeDeviceCommands(int id)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsDevice(id))
	{
		result.push_back("-1");
		result.push_back("Invalid device!");
		return result;
	}

	DataBase::getInstance()->removeDeviceCommands(id);

	result.push_back("1");
	return result;
}

std::vector<std::string> SGui::moveDeviceToGroup(int id,int groupID)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsDevice(id))
	{
		result.push_back("-1");
		result.push_back("Invalid device!");
		return result;
	}

	if(!DataBase::getInstance()->existsGroup(groupID))
	{
		result.push_back("-1");
		result.push_back("Invalid group!");
		return result;
	}

	Device device = DataBase::getInstance()->getDevice(id);
	device.setGroupID(groupID);
	DataBase::getInstance()->updateDevice(device);

	result.push_back("1");
	return result;
}

std::vector<std::string> SGui::applyConfigurationToGroups(int configurationID, std::vector<int> groups)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsConfiguration(configurationID))
	{
		result.push_back("-1");
		result.push_back("Invalid configuration!");
		return result;
	}

	std::vector<Device> devices;
	for(std::vector<int>::iterator i=groups.begin(); i!=groups.end(); i++)
	{
		devices = DataBase::getInstance()->getGroupDevices(*i);
		for(std::vector<Device>::iterator device=devices.begin(); device!=devices.end(); device++)
		{
			device->setConfigID(configurationID);
			if(device->getStatus() == DS_SYNCHRONIZED)
			{
				if(device->getConfigMode() == DCM_MANUAL)
					device->setStatus(DS_PENDING_CONFIRMATION);
				else
					device->setStatus(DS_PENDING_TASKS);
			}
			DataBase::getInstance()->updateDevice(*device);
		}
	}

	result.push_back("1");
	return result;
}

std::vector<std::string> SGui::applyConfigurationToDevices(int configurationID, std::vector<int> devices)
{
	std::vector<std::string> result;

	if(!DataBase::getInstance()->existsConfiguration(configurationID))
	{
		result.push_back("-1");
		result.push_back("Invalid configuration!");
		return result;
	}

	for(std::vector<int>::iterator i=devices.begin(); i!=devices.end(); i++)
	{
		if(DataBase::getInstance()->existsDevice(*i))
		{
			Device device = DataBase::getInstance()->getDevice(*i);
			device.setConfigID(configurationID);
			if(device.getStatus() == DS_SYNCHRONIZED)
			{
				if(device.getConfigMode() == DCM_MANUAL)
					device.setStatus(DS_PENDING_CONFIRMATION);
				else
					device.setStatus(DS_PENDING_TASKS);
			}
			DataBase::getInstance()->updateDevice(device);
		}
	}

	result.push_back("1");
	return result;
}

std::vector<std::string> SGui::getLogs(int level, int offset, int limit)
{
	std::vector<std::string> result;

	std::vector<Log> logs = DataBase::getInstance()->getLastLogs(level,offset,limit);
	for(std::vector<Log>::iterator log = logs.begin(); log !=  logs.end(); log++)
		result.push_back(log->toXML());

	return result;
}

std::vector<std::string> SGui::getDeviceLogs(int deviceID, int level, int offset, int limit)
{
	std::vector<std::string> result;

	std::vector<Log> logs = DataBase::getInstance()->getLastLogs(deviceID,level,offset,limit);
	for(std::vector<Log>::iterator log = logs.begin(); log !=  logs.end(); log++)
		result.push_back(log->toXML());

	return result;
}

std::vector<std::string> SGui::changePassword(std::string oldPass, std::string newPass)
{
	std::vector<std::string> result;

	User user = HttpUserManager::getInstance()->getCurrentUser();
	if(user.validatePassword(oldPass))
	{
		if(newPass.empty())
		{
			result.push_back("0");
			result.push_back("Invalid new password!");
		}
		else
		{
			user.setPassword(newPass,false);
			DataBase::getInstance()->updateUser(user);
			result.push_back("1");
			result.push_back("Password Changed!");
		}
	}
	else
	{
		result.push_back("0");
		result.push_back("Invalid password!");
	}

	return result;
}


std::string SGui::generateBackupSHA(const std::string& filePath, const std::string& restoreOptions)
{
	std::ifstream file(filePath.c_str(),std::ios_base::in);

	if(!file.good())
	{
		Throw(rgcpp::IllegalArgumentException(std::string("Invalid restore file: ")+filePath));
	}

	file.seekg(0,std::ios::end);
	unsigned int size=file.tellg();
	file.seekg(0,std::ios::beg);

	char buffer[16384];
	char digest[SHA_DIGEST_SIZE];
	rgcpp::SHAGenerator shaGenerator;

	shaGenerator.init();

	while(size)
	{
		if(size < 16384)
		{
			file.read(buffer,size);
			shaGenerator.update(buffer,size);
			size = 0;
		}
		else
		{
			file.read(buffer,16384);
			shaGenerator.update(buffer,16384);
			size -= 16384;
		}
	}

	shaGenerator.update(restoreOptions.c_str(),restoreOptions.size());
	shaGenerator.digest(digest);

	return rgcpp::ToHex(digest,SHA_DIGEST_SIZE);
}

}
